package com.self.member.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.self.common.constant.CommonConstants;
import com.self.common.dto.JwtTokenDto;
import com.self.common.exception.BusinessException;
import com.self.member.config.IdAutoConfiguration;
import com.self.member.constant.MemberConstant;
import com.self.member.dto.UserDto;
import com.self.member.enumm.ReviewsStatusEnum;
import com.self.member.feign.Auth2FeignClient;
import com.self.member.geetest.GeetestLib;
import com.self.member.mapper.UserMapper;
import com.self.member.mapstruct.UserDtoMapStruct;
import com.self.member.model.Sms;
import com.self.member.model.User;
import com.self.member.model.UserAuthAuditRecord;
import com.self.member.model.UserAuthInfo;
import com.self.member.service.SmsService;
import com.self.member.service.UserAuthAuditRecordService;
import com.self.member.service.UserAuthInfoService;
import com.self.member.service.UserService;
import com.self.member.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserAuthAuditRecordService userAuthAuditRecordService;
    @Autowired
    private Auth2FeignClient auth2FeignClient;

    @Value("${basic.token:Basic ZXhjaGFuZ2UtYXBpOmV4Y2hhbmdlLXNlY3JldA==}")
    private String basicToken;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private GeetestLib geetestLib;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private UserAuthInfoService userAuthInfoService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private PasswordEncoder passwordEncoder;
   /* @Autowired
    private UserDtoMapStruct userDtoMapStruct;*/


    @Override
    public Page<User> findByPage(Page<User> page, String mobile, Long userId, String userName, String realName, Integer status, Integer reviewsStatus) {
        return page(page,new LambdaQueryWrapper<User>()
                                .eq(userId != null,User::getId,userId)
                                .eq(status != null,User::getStatus,status)
                                .likeRight(StringUtils.hasText(mobile),User::getMobile,mobile)
                                .like(StringUtils.hasText(userName),User::getUsername,userName)
                                .like(StringUtils.hasText(realName),User::getRealName,realName)
                                .eq(reviewsStatus != null,User::getReviewsStatus,reviewsStatus)
        );
    }

    @Override
    public Page<User> findDirectInvitePage(Page<User> page, Long userId) {
        return page(page,new LambdaQueryWrapper<User>().eq(userId != null,User::getDirectInviteid,userId));
    }

    @Override
    public void updateUserAuthStatus(Long id, Byte authStatus, Long authCode, String remark) {
        log.info("开始修改用户的审核状态,当前用户{},用户的审核状态{},图片的唯一code{}",id,authStatus,authCode);
        User user = getById(id);
        if(user!=null){
            user.setReviewsStatus(authStatus.intValue()); // 审核的状态
            updateById(user) ; // 修改用户的状态
            UserAuthAuditRecord userAuthAuditRecord = new UserAuthAuditRecord();
            userAuthAuditRecord.setAuthCode(authCode);
            userAuthAuditRecord.setStatus(authStatus);
            userAuthAuditRecord.setRemark(remark);
            String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
            userAuthAuditRecord.setAuditUserId(Long.parseLong(userId));
            userAuthAuditRecord.setAuditUserName("用户----"+userId);
            userAuthAuditRecord.setUserId(id);
            userAuthAuditRecordService.save(userAuthAuditRecord);
        }

    }

    @Override
    public LoginUserResultVo login(UserLoginVo loginForm) {
        log.info("用户{},开始登录", loginForm.getUsername());
        //校验登陆数据
       // checkLoginData(loginForm);
        loginForm.check(redisTemplate,geetestLib);
        //调用feign登陆
        ResponseEntity<JwtTokenDto> responseEntity = auth2FeignClient.getToken(CommonConstants.GRANT_TYPE_PASSWORD, loginForm.getUsername(), loginForm.getPassword(),
                CommonConstants.MEMBER_TYPE, basicToken);
        if (responseEntity == null || !responseEntity.hasBody()){
            throw new BusinessException("用户登陆获取token异常");
        }
        JwtTokenDto body = responseEntity.getBody();
        log.info("远程调用登陆成功,结果为:{}", JSON.toJSONString(body, true));
        LoginUserResultVo resultVo = new LoginUserResultVo();
        resultVo.setUsername(loginForm.getUsername());
        resultVo.setAccessToken(body.getTokenType()+" "+body.getAccessToken());
        resultVo.setRefreshToken(resultVo.getRefreshToken());
        resultVo.setExpire(body.getExpireIn());
        //保存到缓存
        redisTemplate.opsForValue().set(CommonConstants.JWT_TOKEN_PROFIX+body.getAccessToken().hashCode(),loginForm.getUsername(),
                body.getExpireIn(), TimeUnit.SECONDS);
        return resultVo;
    }

    @Override
    public boolean identifyVerify(Long id, UserAuthForm userAuthForm) {
        User user = getById(id);
        Assert.notNull(user,"用户不存在");
        if (!user.getAuthStatus().equals(MemberConstant.AUTH_STATUS_UNAUTH)){
            throw new BusinessException("该用户已经认证成功了");
        }
        userAuthForm.check(redisTemplate,geetestLib);
        //身份认证
        boolean check = IdAutoConfiguration.check(userAuthForm.getRealName(), userAuthForm.getIdCard());
        if (!check){
            throw new BusinessException("该用户信息错误,请检查");
        }
        //设置用户属性
        user.setAuthtime(new Date());
        user.setAuthStatus(MemberConstant.AUTH_STATUS_PRIMARY);
        user.setIdCard(userAuthForm.getIdCard());
        user.setIdCardType(userAuthForm.getIdCardType());
        user.setRealName(userAuthForm.getRealName());
        updateById(user);
        return true;
    }


    @Override
    public User getById(Serializable id) {
        User user = super.getById(id);
        Assert.notNull(user,"用户不存在");
        //审核状态,1通过,2拒绝,0,待审核
        Integer reviewsStatus = user.getReviewsStatus();
        if (reviewsStatus == null){
            user.setSeniorAuthStauts(Byte.valueOf(ReviewsStatusEnum.REVIEWSSTATUS_UNADD.getValue()+""));
            user.setSeniorAuthDesc(ReviewsStatusEnum.REVIEWSSTATUS_UNADD.getDesc());
        }else {
            if (ReviewsStatusEnum.REVIEWSSTATUS_REJECT.equals(reviewsStatus)){
                user.setSeniorAuthStauts(Byte.valueOf(reviewsStatus+""));
                List<UserAuthAuditRecord> authAuditRecordList = userAuthAuditRecordService.getAuthAuditRecordList(user.getId());
                if (!CollectionUtils.isEmpty(authAuditRecordList)){
                    user.setSeniorAuthDesc(authAuditRecordList.get(0).getRemark());
                }else{
                    user.setSeniorAuthDesc("未知原因");
                }
            }else{
                user.setSeniorAuthStauts(Byte.valueOf(reviewsStatus+""));
                user.setSeniorAuthDesc(ReviewsStatusEnum.getDescByVal(reviewsStatus));
            }
        }
        return user;
    }

    @Transactional
    @Override
    public void authUser(Long id, List<String> imgs) {
        if (CollectionUtils.isEmpty(imgs)){
             throw new BusinessException("上传图片不能为空");
        }
        User user = super.getById(id);
        Assert.notNull(user,"用户不存在");
        List<UserAuthInfo> list = Lists.newArrayList();
        int seriNo = 0;
        for (String img : imgs){
            UserAuthInfo userAuthInfo = new UserAuthInfo();
            userAuthInfo.setUserId(id);
            userAuthInfo.setSerialno(seriNo++);
            userAuthInfo.setImageUrl(img);
            userAuthInfo.setAuthCode(snowflake.nextId());
            list.add(userAuthInfo);
        }
        userAuthInfoService.saveBatch(list);
        user.setReviewsStatus(MemberConstant.AUDIT_STATUS_UNAUDIT);
        updateById(user);
    }

    @Override
    public boolean updatePhone(Long id, UpdatePhoneParam updatePhoneParam) {
        User user = super.getById(id);
        Assert.notNull(user,"用户不存在");
        //验证旧手机号验证码
        String oldVerify = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_VERIFY_OLD_PHONE + ":" + user.getMobile());
        Assert.isTrue(updatePhoneParam.getOldValidateCode().equals(oldVerify),"旧手机的验证码错误");
        //验证新手机号验证码
        String newVerify = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_CHANGE_PHONE_VERIFY + ":" + updatePhoneParam.getNewMobilePhone());
        Assert.isTrue(updatePhoneParam.getValidateCode().equals(newVerify),"新手机的验证码错误");
        // 4 修改手机号
        user.setMobile(updatePhoneParam.getNewMobilePhone());
        return updateById(user);
    }

    @Override
    public boolean checkNewPhone(String mobile, String countryCode) {
        //1 新的手机号,没有旧的用户使用
        int count = count(new LambdaQueryWrapper<User>().eq(User::getMobile, mobile).eq(User::getCountryCode,countryCode));
        Assert.isTrue(count <= 0,"该手机号已经被占用");
        // 2 向新的手机发送短信
        Sms sms = new Sms();
        sms.setMobile(mobile);
        sms.setCountryCode(countryCode);
        sms.setTemplateCode(MemberConstant.SMS_CHANGE_PHONE_VERIFY); // 模板代码  -- > 校验手机号
        return smsService.sendMsg(sms) ;
    }

    @Override
    public boolean updateUserPayPwd(Long userId, UpdateLoginParam updateLoginParam) {
        User user = super.getById(userId);
        Assert.notNull(user,"用户不存在");
        boolean matches = passwordEncoder.matches(updateLoginParam.getOldpassword(), user.getPassword());
        Assert.isTrue(matches,"用户的原始密码输入错误");
        String loginVerify = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_CHANGE_PAY_PWD_VERIFY + ":" + user.getMobile());
        Assert.isTrue(updateLoginParam.getValidateCode().equals(loginVerify),"手机验证码错误");
        user.setPaypassword(passwordEncoder.encode(updateLoginParam.getNewpassword())); // 修改为加密后的密码
        return updateById(user);
    }

    @Override
    public boolean unsetPayPassword(Long userId, UnsetPayPasswordParam unsetPayPasswordParam) {
        User user = super.getById(userId);
        Assert.notNull(user,"用户不存在");
        String unsetVerify = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_FORGOT_PAY_PWD_VERIFY + ":" + user.getMobile());
        Assert.isTrue(unsetPayPasswordParam.getValidateCode().equals(unsetVerify),"手机验证码错误");
        user.setPaypassword(passwordEncoder.encode(unsetPayPasswordParam.getPayPassword())); // 修改为加密后的密码
        return updateById(user);
    }

    @Override
    public boolean updateUserLoginPwd(Long userId, UpdateLoginParam updateLoginParam) {
        User user = super.getById(userId);
        Assert.notNull(user,"用户不存在");
        boolean matches = passwordEncoder.matches(updateLoginParam.getOldpassword(), user.getPassword());
        Assert.isTrue(matches,"用户的原始密码输入错误");
        String loginVerify = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_CHANGE_LOGIN_PWD_VERIFY + ":" + user.getMobile());
        Assert.isTrue(updateLoginParam.getValidateCode().equals(loginVerify),"手机验证码错误");
        user.setPassword(passwordEncoder.encode(updateLoginParam.getNewpassword())); // 修改为加密后的密码
        return updateById(user);
    }

    @Override
    public List<User> getUserInvites(Long userId) {
        List<User> list = list(new LambdaQueryWrapper<User>().eq(User::getDirectInviteid, userId));
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyList();
        }
        list.forEach(user ->{
            user.setPaypassword("**********");
            user.setPassword("**********");
            user.setGaSecret("**********");
            user.setAccessKeyId("**********");
            user.setAccessKeySecret("**********");
        });
        return list;
    }

    @Override
    public List<UserDto> getBasicUsers(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)){
            return Collections.emptyList();
        }
        List<User> list = list(new LambdaQueryWrapper<User>().in(User::getId, ids));
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyList();
        }
        return UserDtoMapStruct.INSTANCE.convert2Dto(list);
    }

    @Override
    public Map<Long, UserDto> getBasicUsersByUsername(List<Long> ids, String username, String mobile) {
        if (CollectionUtils.isEmpty(ids) && org.apache.commons.lang3.StringUtils.isAllEmpty(username,mobile)){
            return Collections.emptyMap();
        }
        List<User> list = list(new LambdaQueryWrapper<User>()
                .in(User::getId, ids)
                .likeRight(!StringUtils.isEmpty(username),User::getUsername,username)
                .likeRight(!StringUtils.isEmpty(mobile),User::getMobile,mobile));
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyMap();
        }
        List<UserDto> userDtos = UserDtoMapStruct.INSTANCE.convert2Dto(list);
       return userDtos.stream().collect(Collectors.toMap(UserDto::getId, x -> x, (t1, t2) -> t1));
    }

    @Override
    public boolean register(RegisterParam registerParam) {
        log.info("用户开始注册{}", JSON.toJSONString(registerParam, true));
        String email = registerParam.getEmail();
        String mobile = registerParam.getMobile();
        Assert.isTrue(StringUtils.hasText(email) || StringUtils.hasText(mobile),"手机号或邮箱不能同时为空");
        int count = count(new LambdaQueryWrapper<User>()
                .eq(!StringUtils.isEmpty(email), User::getEmail, email)
                .eq(!StringUtils.isEmpty(mobile), User::getMobile, mobile));
        Assert.isTrue(count <=0,"手机号或邮箱已经被注册");
        // 进行极验的校验
        registerParam.check(redisTemplate,geetestLib);
        User user = getUser(registerParam);
        return save(user);
    }

    private User getUser(RegisterParam registerParam) {
        User user = new User();
        user.setCountryCode(registerParam.getCountryCode());
        user.setEmail(registerParam.getEmail());
        user.setMobile(registerParam.getMobile());
        String encodePwd = new BCryptPasswordEncoder().encode(registerParam.getPassword());
        user.setPassword(encodePwd);
        user.setPaypassSetting(false);
        user.setStatus(Byte.valueOf(MemberConstant.STATUS_ENABLE+""));
        user.setType(Byte.valueOf(MemberConstant.STATUS_ENABLE+""));
        user.setAuthStatus(MemberConstant.AUTH_STATUS_UNAUTH);
        user.setLogins(0);
        user.setInviteCode(RandomUtil.randomString(6)); // 用户的邀请码
        if (!StringUtils.isEmpty(registerParam.getInvitionCode())){
            User one = getOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, registerParam.getInvitionCode()));
            if (one != null){
                user.setDirectInviteid(one.getId()+"");
                user.setInviteRelation(one.getId()+"");
            }
        }
        return user;
    }

    @Override
    public boolean unsetLoginPwd(UnSetPasswordParam unSetPasswordParam) {
        log.info("开始重置密码{}", JSON.toJSONString(unSetPasswordParam, true));
        unSetPasswordParam.check(redisTemplate,geetestLib);
        String validCode = (String)redisTemplate.opsForValue().get(MemberConstant.CACHE_SMS_PROFIX + MemberConstant.SMS_FORGOT_VERIFY + ":" + unSetPasswordParam.getMobile());
        Assert.isTrue(unSetPasswordParam.getValidateCode().equals(validCode),"手机验证码错误");
        User one = getOne(new LambdaQueryWrapper<User>().eq(User::getMobile, unSetPasswordParam.getMobile()));
        Assert.notNull(one,"该用户不存在");
        one.setPassword(passwordEncoder.encode(unSetPasswordParam.getPassword()));
        return updateById(one);
    }
}
